/**
 * 
 */
package com.googlecode.cswish.model;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;

import org.apache.commons.lang.ClassUtils;
import org.apache.log4j.Logger;

import com.googlecode.cswish.annotation.GenericType;
import com.googlecode.cswish.util.ClassUtil;

/**
 * @author Jerry.Feng Date: 2008-4-10
 *
 */
public class PropertyInfo {
	
	private static final Logger logger = Logger.getLogger(PropertyInfo.class);
	
	///////////////////////////// class information ///////////////////////////
	private String name;
	
	private String type;
	
	// available only when it isn't simple object
	private String genericType;
	
	private Method readMethod;
	
	private Method writeMethod;
	
	private boolean isSimpleObj;
	
	//////////////////////////// table information ////////////////////////////
	/**
	 * the field mapped to a table column
	 */
	private boolean tableColumn;
	
	private String columnName;
	
	private boolean isId;
	
	// id is generated by hibernate automatically
	private boolean autoId;
	
	private boolean unique;
	
	private boolean nullable;
	
	private boolean insertable;
	
	private boolean updatable;
	
	private int length;
	
	private boolean isDynamic;
	
	///////////////////////////// page information ////////////////////////////
	private int tabIndex;
	
	///////////////////////////// query information ///////////////////////////
	/**
	 * the field supports "between ? and ?"
	 */
	private boolean searchInRange;

	public PropertyInfo() {
	}
	
	public PropertyInfo(String className, String name) {
		setType(className);
		setName(name);
		
		Class<?> type = getTypeClass();
		if (type != null) {
			boolean isSimpleObj = type.isPrimitive() || Number.class.isAssignableFrom(type)
				|| String.class.equals(type) || Date.class.isAssignableFrom(type);
			setSimpleObj(isSimpleObj);
		}
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getColumnName() {
		return columnName;
	}

	public void setColumnName(String columnName) {
		this.columnName = columnName;
	}

	/**
	 * column type
	 * 
	 * @return
	 */
	public String getType() {
		return type;
	}
	
	public Class<?> getTypeClass() {
		try {
			return ClassUtils.getClass(type);
		} catch (ClassNotFoundException ex) {
			logger.debug("type is not valid static class:" + type);
			return null;
		}
	}

	public void setType(String type) {
		this.type = type;
		
		// Default generic type is same with type
		if (this.genericType == null) {
			this.genericType = this.type;
		}
	}

	public Method getReadMethod() {
		return readMethod;
	}
	
	public Object getValue(Object instance) {
		Object value = null;
		if (isDynamic()) {
			if (instance instanceof Map) {
				value = ((Map)instance).get(getName());
			}
			// TODO: process the Fixed Model
		} else if (readMethod != null) {
			try {
				Object[] params = null;
				value = readMethod.invoke(instance, params);
			} catch (Exception ex) {
				logger.error("Can't get value by method:" + readMethod.getName(), ex);
			}
		}
		return value;
	}
	
	public void setValue(Object instance, Object value) {
		if (isDynamic()) {
			if (instance instanceof Map) {
				value = ((Map)instance).put(getName(), value);
			}
			// TODO: process the Fixed Model
		} else	if (writeMethod != null) {
			try {
				writeMethod.invoke(instance, value);
			} catch (Exception ex) {
				logger.error("Can't get value", ex);
			}
		}
	}

	public void setReadMethod(Method readMethod) {
		this.readMethod = readMethod;
	}

	public Method getWriteMethod() {
		return writeMethod;
	}

	public void setWriteMethod(Method writeMethod) {
		this.writeMethod = writeMethod;
	}

	public boolean isSimpleObj() {
		return isSimpleObj;
	}

	public void setSimpleObj(boolean isSimpleObj) {
		this.isSimpleObj = isSimpleObj;
	}

	public boolean isTableColumn() {
		return tableColumn;
	}

	public void setTableColumn(boolean tableColumn) {
		this.tableColumn = tableColumn;
	}

	public boolean isUnique() {
		return unique;
	}

	public void setUnique(boolean unique) {
		this.unique = unique;
	}

	public boolean isNullable() {
		return nullable;
	}

	public void setNullable(boolean nullable) {
		this.nullable = nullable;
	}

	public boolean isInsertable() {
		return insertable;
	}

	public void setInsertable(boolean insertable) {
		this.insertable = insertable;
	}

	public boolean isUpdatable() {
		return updatable;
	}

	public void setUpdatable(boolean updatable) {
		this.updatable = updatable;
	}

	public int getLength() {
		return length;
	}

	public void setLength(int length) {
		this.length = length;
	}

	public boolean isDynamic() {
		return isDynamic;
	}

	public void setDynamic(boolean isDynamic) {
		this.isDynamic = isDynamic;
	}

	/**
	 * FIXME: move the property to model class  
	 * @return
	 */
	public int getTabIndex() {
		return tabIndex;
	}

	public void setTabIndex(int tabIndex) {
		this.tabIndex = tabIndex;
	}

	public boolean isSearchInRange() {
		return searchInRange;
	}

	public void setSearchInRange(boolean searchInRange) {
		this.searchInRange = searchInRange;
	}

	public boolean isId() {
		return isId;
	}

	public void setId(boolean isId) {
		this.isId = isId;
	}

	public String getGenericType() {
		return ClassUtil.getRuntimeType(genericType);
	}
	
	public Class<?> getGenericTypeClass() {
		try {
			return ClassUtils.getClass(getGenericType());
		} catch (ClassNotFoundException ex) {
			logger.debug("generic type is not valid static class:" + genericType);
			return Object.class;
		}
	}

	public void setGenericType(String genericType) {
		if (GenericType.MODEL.equals(genericType) || "java.lang.Object".equals(genericType)) {
			this.genericType = GenericType.MODEL;
		} else if (GenericType.PARAM_MODEL.equals(genericType)) {
			this.genericType = GenericType.PARAM_MODEL;
		} else {
			this.genericType = genericType;
		}
	}

	public void setAutoId(boolean autoId) {
		this.autoId = autoId;
	}

	public boolean isAutoId() {
		return autoId;
	}
	
	public String toString() {
		return (this.type != null ? this.type : "") + '<' 
			+ (this.genericType != null ? this.genericType : "") + "> "
			+ (this.name != null ? this.name : "");
	}
	
	public PropertyInfo clone() {
		PropertyInfo propertyInfo = new PropertyInfo();
		propertyInfo.setName(this.name);
		propertyInfo.setType(this.type);
		propertyInfo.setSimpleObj(this.isSimpleObj);
		propertyInfo.setGenericType(this.genericType);
		propertyInfo.setAutoId(this.autoId);
		propertyInfo.setDynamic(this.isDynamic);
		propertyInfo.setId(this.isId);
		propertyInfo.setInsertable(this.insertable);
		propertyInfo.setLength(this.length);
		propertyInfo.setNullable(this.nullable);
		propertyInfo.setReadMethod(this.readMethod);
		propertyInfo.setSearchInRange(this.searchInRange);
		propertyInfo.setTabIndex(this.tabIndex);
		propertyInfo.setTableColumn(this.tableColumn);
		propertyInfo.setUnique(this.unique);
		propertyInfo.setUpdatable(this.updatable);
		propertyInfo.setWriteMethod(this.writeMethod);
		return propertyInfo;
	}
}